{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- https://course.rs/advance-practice/intro.html\n",
    "\n",
    "在入门实战，你可以说众览半山不咋小，但是能坚持到这里，甚至当完成后，就真的是一览众山小，余敌皆鱼虾了。\n",
    "\n",
    "在进阶实战中，我们要来真的了，之前的简单命令行程序，是真的简单，但是这次的简单 redis 是真的不简单，在这里你将被迫使用十八般武艺，特别的，我们还将学会 Rust 异步镇山之宝 `tokio` 包的使用。\n",
    "\n",
    "> 本章在内容上大量借鉴和翻译了 tokio 官方文档[Tokio Tutorial](https://tokio.rs/tokio/tutorial), 但是重新组织了内容形式并融入了很多自己的见解和感悟，给大家提供更好的可读性和知识扩展性"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# tokio 概览\n",
    "\n",
    "- https://course.rs/advance-practice/overview.html\n",
    "\n",
    "**tl;dr — Tokio 概览**\n",
    "\n",
    "- Rust 自带的 async/await、`Future` 等只是语法和抽象，要真正“跑起来”必须依赖第三方异步运行时。\n",
    "- 异步运行时核心 = reactor（监听 I/O、定时器等事件） + executor（调度/执行 `Future`）。\n",
    "- 常见运行时有 Tokio、async-std、smol，其中 **Tokio** 凭借性能、生态、文档、社区基本成了事实标准。\n",
    "- 选择运行时会深度绑定其生态，切换成本极高，因此通常一开始就选 Tokio；如需跨运行时可借助 `async-compat`。\n",
    "- **Tokio 优势**：极致性能、高可靠性、一致性行为，API 贴近标准库、生态丰富（tracing、console 等）、运行时配置灵活（多线程/单线程）。\n",
    "- **Tokio 局限**：不适合纯 CPU 密集任务（需用 `spawn_blocking` 或 rayon）、文件 IO 并发收益有限、小量 HTTP 请求过度复杂。\n",
    "- 关键认知：async/await 需要运行时支撑；Tokio 是 Rust 异步的首选目标，值得长期投入学习。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# tokio 初印象\n",
    "\n",
    "又到了喜闻乐见的初印象环节，这个环节决定了你心中的那 24 盏灯最终是全亮还是全灭。\n",
    "\n",
    "在本文中，我们将看看本专题的学习目标、tokio该怎么引入以及如何实现一个 Hello Tokio 项目，最终亮灯还是灭灯的决定权留给各位看官。但我提前说好，如果你全灭了，但却找不到更好的，未来还是得回来真香 :P"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [专题目标](https://course.rs/advance-practice/getting-startted.html#专题目标)\n",
    "\n",
    "通过 API 学项目无疑是无聊的，因此我们采用一个与众不同的方式：边学边练，在本专题的最后你将拥有一个 `redis` 客户端和服务端，当然不会实现一个完整版本的 `redis` ，只会提供基本的功能和部分常用的命令。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [mini-redis](https://course.rs/advance-practice/getting-startted.html#mini-redis)\n",
    "\n",
    "`redis` 的项目源码可以在[这里访问](https://github.com/sunface/rust-by-practice/tree/master/zh-CN/assets/mini-redis)，本项目是从[官方地址](https://github.com/tokio-rs/mini-redis) `fork` 而来，在未来会提供注释和文档汉化。\n",
    "\n",
    "再次声明：该项目仅仅用于学习目的，因此它的文档注释非常全，但是它完全无法作为 `redis` 的替代品。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [环境配置](https://course.rs/advance-practice/getting-startted.html#环境配置)\n",
    "\n",
    "首先，我们假定你已经安装了 Rust 和相关的工具链，例如 `cargo`。其中 Rust 版本的最低要求是 `1.45.0`，建议使用最新版 `1.58`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "rustc 1.90.0 (1159e78c4 2025-09-14)\n"
     ]
    }
   ],
   "source": [
    "! rustc --version"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来，安装 `mini-redis` 的服务器端，它可以用来测试我们后面将要实现的 `redis` 客户端："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "! cargo install mini-redis"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> 如果下载失败，也可以通过[这个地址](https://github.com/sunface/rust-by-practice/tree/master/zh-CN/assets/mini-redis)下载源码，然后在本地通过 `cargo run`运行。\n",
    "\n",
    "下载成功后，启动服务端:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "! sudo netstat -tunlp | grep 6379"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "! mini-redis-server --port 6380"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然后，再使用客户端测试下刚启动的服务端:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OK\n"
     ]
    }
   ],
   "source": [
    "! mini-redis-cli set foo 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\"1\"\n"
     ]
    }
   ],
   "source": [
    "! mini-redis-cli get foo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然后，再使用客户端测试下刚启动的服务端:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [Hello Tokio](https://course.rs/advance-practice/getting-startted.html#hello-tokio)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [分析未到，代码先行](https://course.rs/advance-practice/getting-startted.html#分析未到代码先行)\n",
    "\n",
    "在详细讲解之前，我们先来看看完整的代码，让大家有一个直观的印象。首先，创建一个新的 `Rust` 项目:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "    Creating binary (application) `my-redis` package\n",
      "note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n"
     ]
    }
   ],
   "source": [
    "! cargo new my-redis"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然后在 `Cargo.toml` 中添加相关的依赖:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "[dependencies]\n",
    "tokio = { version = \"1\", features = [\"full\"] }\n",
    "mini-redis = \"0.4\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来，使用以下代码替换 `main.rs` 中的内容：\n",
    "\n",
    "- [main.rs](./my-redis/src/main.rs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "vscode": {
     "languageId": "toml"
    }
   },
   "source": [
    "最后，运行这个项目:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "从服务器端获取到结果=Some(b\"world\")\n"
     ]
    }
   ],
   "source": [
    "! cd my-redis && cargo run -q"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [原理解释](https://course.rs/advance-practice/getting-startted.html#原理解释)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "代码篇幅虽然不长，但是还是有不少值得关注的地方，接下来我们一起来看看。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "let mut client = client::connect(\"127.0.0.1:6379\").await?;"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[`client::connect`](https://docs.rs/mini-redis/0.4.1/mini_redis/client/fn.connect.html) 函数由`mini-redis` 包提供，它使用异步的方式跟指定的远程 `IP` 地址建立 TCP 长连接，一旦连接建立成功，那 `client` 的赋值初始化也将完成。\n",
    "\n",
    "特别值得注意的是：虽然该连接是异步建立的，但是从代码本身来看，完全是**同步的代码编写方式**，唯一能说明异步的点就是 `.await`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [什么是异步编程](https://course.rs/advance-practice/getting-startted.html#什么是异步编程)\n",
    "\n",
    "大部分计算机程序都是按照代码编写的顺序来执行的：先执行第一行，然后第二行，以此类推(当然，还要考虑流程控制，例如循环)。当进行同步编程时，一旦程序遇到一个操作无法被立即完成，它就会进入阻塞状态，直到该操作完成为止。\n",
    "\n",
    "因此同步编程非常符合我们人类的思维习惯，是一个顺其自然的过程，被几乎每一个程序员所喜欢(本来想说所有，但我不敢打包票，毕竟总有特立独行之士)。例如，当建立 TCP 连接时，当前线程会被阻塞，直到等待该连接建立完成，然后才往下继续进行。\n",
    "\n",
    "而使用异步编程，无法立即完成的操作会被切到后台去等待，因此当前线程不会被阻塞，它会接着执行其它的操作。一旦之前的操作准备好可以继续执行后，它会通知执行器，然后执行器会调度它并从上次离开的点继续执行。但是大家想象下，如果没有使用 `await`，而是按照这个异步的流程使用通知 -> 回调的方式实现，代码该多么的难写和难读！\n",
    "\n",
    "好在 Rust 为我们提供了 `async/await` 的异步编程特性，让我们可以像写同步代码那样去写异步的代码，也让这个世界美好依旧。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [编译时绿色线程](https://course.rs/advance-practice/getting-startted.html#编译时绿色线程)\n",
    "\n",
    "一个函数可以通过`async fn`的方式被标记为异步函数:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use mini_redis::Result;\n",
    "use mini_redis::client::Client;\n",
    "use tokio::net::ToSocketAddrs;\n",
    "\n",
    "pub async fn connect<T: ToSocketAddrs>(addr: T) -> Result<Client> {\n",
    "    // ...\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在上例中，`redis` 的连接函数 `connect` 实现如上，它看上去很像是一个同步函数，但是 `async fn` 出卖了它。 `async fn` 异步函数并不会直接返回值，而是返回一个 `Future`，顾名思义，该 `Future` 会在未来某个时间点被执行，然后最终获取到真实的返回值 `Result<Client>`。\n",
    "\n",
    "> async/await 的原理就算大家不理解，也不妨碍使用 `tokio` 写出能用的服务，但是如果想要更深入的用好，强烈建议认真读下本书的 [`async/await` 异步编程章节](https://course.rs/advance/async/intro.html)，你会对 Rust 的异步编程有一个全新且深刻的认识。\n",
    "\n",
    "由于 `async` 会返回一个 `Future`，因此我们还需要配合使用 `.await` 来让该 `Future` 运行起来，最终获得返回值:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    ":dep tokio = { version = \"1.48.0\", features = [\"full\"] }\n",
    "\n",
    "async fn say_to_world() -> String {\n",
    "    String::from(\"world\")\n",
    "}\n",
    "\n",
    "#[tokio::main]\n",
    "async fn main() {\n",
    "    // 此处的函数调用是惰性的，并不会执行 `say_to_world()` 函数体中的代码\n",
    "    let op = say_to_world();\n",
    "\n",
    "    // 首先打印出 \"hello\"\n",
    "    println!(\"hello\");\n",
    "\n",
    "    // 使用 `.await` 让 `say_to_world` 开始运行起来\n",
    "    println!(\"{}\", op.await);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面代码输出如下:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello\n",
      "world\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "而大家可能很好奇 `async fn` 到底返回什么吧？它实际上返回的是一个实现了 `Future` 特征的匿名类型: `impl Future<Output = String>`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [async main](https://course.rs/advance-practice/getting-startted.html#async-main)\n",
    "\n",
    "在代码中，使用了一个与众不同的 `main` 函数 : `async fn main` ，而且是用 `#[tokio::main]` 属性进行了标记。异步 `main` 函数有以下意义：\n",
    "\n",
    "- `.await` 只能在 `async` 函数中使用，如果是以前的 `fn main`，那它内部是无法直接使用 `async` 函数的！这个会极大的限制了我们的使用场景\n",
    "- 异步运行时本身需要初始化\n",
    "\n",
    "因此 `#[tokio::main]` 宏在将 `async fn main` 隐式的转换为 `fn main` 的同时还对整个异步运行时进行了初始化。例如以下代码:"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "C++17",
   "language": "C++17",
   "name": "xcpp17"
  },
  "language_info": {
   "codemirror_mode": "rust",
   "file_extension": ".rs",
   "mimetype": "text/rust",
   "name": "C++17",
   "pygment_lexer": "rust",
   "version": ""
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
